package games.mapacman.server;

import games.mapacman.common.Constants;
import games.mapacman.common.Dot;
import games.mapacman.common.EatenSign;
import games.mapacman.common.Fruit;
import games.mapacman.common.Powerpill;
import games.mapacman.common.ScoreSign;
import games.mapacman.common.Sign;
import games.mapacman.common.Superdot;
import games.mapacman.common.ZoneChangePoint;

import java.awt.Point;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Random;
import java.util.Vector;

import marauroa.common.game.RPObject;
import marauroa.server.game.rp.MarauroaRPZone;

public class MaPacmanZone extends MarauroaRPZone {

    private boolean[][] collision;
    private String zonename;
    private MaPacmanRPWorld world;
    private Dot[][] dots;
    private Vector<RPObject> Ghosts;
    private Vector<Dot> respawnDots;
    private Vector<RPObject> Players;
    private Vector<Point> RespawnPoints;
    private Vector<Point> GhostRespawnPoints;
    private Vector<Point> GhostBlock;
    private Vector<Sign> Signs;
    private Vector<ZoneChangePoint> zoneChangePoints;
    private Vector<String> zoneChangeNames;
    private Vector<zoneChangeNote> zoneChangeNotes;
    private Vector<RPObject> addMovablesNextTurn;
    private Point zoneSize;
    private Random randomgen;
    private int dot_respawnTime = 150;
    private int superdot_respawnTime = 350;
    private int fruit_respawnTime = 500;
    private int powerpill_respawnTime = 700;
    private int dot_score = 1;
    private int superdot_score = 5;
    private int fruit_score = 50;
    private int powerpill_score = 0;
    // Probability that SuperDot is Superdot ... otherwise it's normal dot
    // prob=1 means every sdot is superdot ... prob =3 means every third Sdot is
    // Sdot
    private int superdot_prob = 1;
    private int fruit_prob = 30;
    private int powerpill_prob = 1;

    public MaPacmanZone(String zoneName, MaPacmanRPWorld world) {

        super(zoneName);

        this.randomgen = world.getRandom();
        this.Ghosts = new Vector<RPObject>();
        this.addMovablesNextTurn = new Vector<RPObject>();
        this.respawnDots = new Vector<Dot>();
        this.Players = new Vector<RPObject>();
        this.RespawnPoints = new Vector<Point>();
        this.GhostRespawnPoints = new Vector<Point>();
        this.GhostBlock = new Vector<Point>();
        this.Signs = new Vector<Sign>();
        this.zoneChangeNames = new Vector<String>();
        this.zoneChangePoints = new Vector<ZoneChangePoint>();
        // save zoneChanges here to do when turn ends
        this.zoneChangeNotes = new Vector<zoneChangeNote>();
        this.zonename = zoneName;
        this.world = world;
        world.addRPZone(this);


    }

    public void initialize() { /*
         * # Wall Dot g Ghost without Dot G Ghost with
         * Dot r respawn without Dot R respawn with Dot
         * F Fruit P Powerpill 0 to 9 ZoneChange Points
         * - Block without Dot _ Block with Dot +
         * Superdot
         */

        try {
            BufferedReader map = new BufferedReader(new InputStreamReader(
                    this.getClass().getResourceAsStream("maps/" + zonename + ".map")));

            int x = 100;
            int y = 100;
            boolean mapstart = false;
            String line;
            int ypos = 0;
            while ((line = map.readLine()) != null) {
                if (mapstart) {
                    for (int xpos = 0; xpos < line.length(); xpos++) {
                        char currentItem = line.charAt(xpos);
                        if (currentItem == '#') { // is WALL
                            collision[xpos][ypos] = true;
                            RPObject wall = new RPObject();
                            wall.put("type", Constants.TYPE_WALL);
                            wall.put("x", xpos);
                            wall.put("y", ypos);
                            wall.put("zoneid", this.zonename);
                            world.add(wall);
                        } else { // NO WALL
                            collision[xpos][ypos] = false;
                            if ((currentItem == 'G') || (currentItem == 'g')) { // is
                                // GHOST
                                if (currentItem == 'G') {
                                    currentItem = '*';
                                }
                                RPObject ghost = new RPObject();
                                ghost.put("type", Constants.TYPE_GHOST);
                                ghost.put("x", xpos);
                                ghost.put("y", ypos);
                                ghost.put("dir", Constants.DIR_NORTH);
                                ghost.put("nextdir", Constants.DIR_NORTH);
                                ghost.put("!changedir", 10);
                                ghost.put("zoneid", this.zonename);
                                ghost.put("color", randomgen.nextInt(4));
                                Ghosts.add(ghost);
                                world.add(ghost);
                                GhostRespawnPoints.add(new Point(xpos, ypos));
                            } else if ((currentItem == 'R')
                                    || (currentItem == 'r')) { // is
                                // RespawnPoint
                                if (currentItem == 'R') {
                                    currentItem = '*';
                                }
                                RespawnPoints.add(new Point(xpos, ypos));
                            } else if ((currentItem == '-')
                                    || (currentItem == '_')) { // is GhostBlock
                                if (currentItem == '_') {
                                    currentItem = '*';
                                }
                                GhostBlock.add(new Point(xpos, ypos));
                            } else if (currentItem == '+') { // is SuperDot
                                Dot sdot = new Superdot(xpos, ypos, dot_score,
                                        superdot_score, superdot_prob, this);
                                dots[xpos][ypos] = sdot;
                                world.add(sdot.getRPObject());
                            } else if (currentItem == 'P') { // is Powerpill
                                Dot pill = new Powerpill(xpos, ypos, dot_score,
                                        powerpill_score, powerpill_prob, this);
                                dots[xpos][ypos] = pill;
                                world.add(pill.getRPObject());
                            } else if (currentItem == 'F') { // is Fruit
                                Dot fruit = new Fruit(xpos, ypos, dot_score,
                                        fruit_score, fruit_prob, this);
                                dots[xpos][ypos] = fruit;
                                world.add(fruit.getRPObject());
                            }

                            // check for Dot at last because other Items can
                            // have Dot below
                            if (currentItem == '*') { // is DOT
                                Dot dot = new Dot(xpos, ypos, dot_score, this);
                                dots[xpos][ypos] = dot;
                                world.add(dot.getRPObject());
                            } else { // ZoneChange Point
                                if ((currentItem >= 48) && (currentItem <= 57)) {
                                    ZoneChangePoint point = new ZoneChangePoint(
                                            this, zoneChangeNames.get(currentItem - 48),
                                            xpos, ypos);
                                    zoneChangePoints.add(point);
                                    world.add(point.getRPObject());
                                }
                            }
                        }
                    }
                    ypos++;
                } else // MAP INFO didn't start yet
                {
                    if (line.startsWith("x=")) {
                        x = Integer.parseInt(line.substring(2));
                    } else if (line.startsWith("y=")) {
                        y = Integer.parseInt(line.substring(2));
                    } else if (line.startsWith("y=")) {
                        y = Integer.parseInt(line.substring(2));
                    } else if (line.startsWith("dot_respawnTime=")) {
                        dot_respawnTime = Integer.parseInt(line.substring("dot_respawnTime=".length()));
                    } else if (line.startsWith("superdot_respawnTime=")) {
                        superdot_respawnTime = Integer.parseInt(line.substring("superdot_respawnTime=".length()));
                    } else if (line.startsWith("fruit_respawnTime=")) {
                        fruit_respawnTime = Integer.parseInt(line.substring("fruit_respawnTime=".length()));
                    } else if (line.startsWith("powerpill_respawnTime=")) {
                        powerpill_respawnTime = Integer.parseInt(line.substring("powerpill_respawnTime=".length()));
                    } else if (line.startsWith("dot_Score=")) {
                        dot_score = Integer.parseInt(line.substring("dot_Score=".length()));
                    } else if (line.startsWith("superdot_Score=")) {
                        superdot_score = Integer.parseInt(line.substring("superdot_Score=".length()));
                    } else if (line.startsWith("fruit_Score=")) {
                        fruit_score = Integer.parseInt(line.substring("fruit_Score=".length()));
                    } else if (line.startsWith("powerpill_Score=")) {
                        powerpill_score = Integer.parseInt(line.substring("powerpill_Score=".length()));
                    } else if (line.startsWith("mapchange=")) {
                        zoneChangeNames.add(line.substring("mapchange=".length()));
                    } else if (line.startsWith("superdot_probability=")) {
                        superdot_prob = Integer.parseInt(line.substring("superdot_probability=".length()));
                    } else if (line.startsWith("fruit_probability=")) {
                        fruit_prob = Integer.parseInt(line.substring("fruit_probability=".length()));
                    } else if (line.startsWith("powerpill_probability=")) {
                        powerpill_prob = Integer.parseInt(line.substring("powerpill_probability=".length()));
                    } else if (line.startsWith("MAPSTART")) {
                        mapstart = true;
                        collision = new boolean[x][y];
                        dots = new Dot[x][y];
                    }
                }
            }

            setZoneSize(new Point(x, y));
        } catch (Exception e) {
            // TODO: throw the exception to the caller so it knows about the
            // problem.
            e.printStackTrace();
        }
    }

    public Point getZoneSize() {
        return zoneSize;
    }

    public void setZoneSize(Point zoneSize) {
        this.zoneSize = zoneSize;
    }

    

    public void initializeForLB() { /*
         * # Wall Dot g Ghost without Dot G Ghost with
         * Dot r respawn without Dot R respawn with Dot
         * F Fruit P Powerpill 0 to 9 ZoneChange Points
         * - Block without Dot _ Block with Dot +
         * Superdot
         */

        try {
            BufferedReader map = new BufferedReader(new InputStreamReader(
                    this.getClass().getResourceAsStream("maps/" + zonename + ".map")));

            int x = 100;
            int y = 100;
            boolean mapstart = false;
            String line;
            int ypos = 0;
            while ((line = map.readLine()) != null) {
                if (mapstart) {
                    for (int xpos = 0; xpos < line.length(); xpos++) {
                        char currentItem = line.charAt(xpos);
                        if (currentItem == '#') { // is WALL
                            collision[xpos][ypos] = true;
                        } else { // NO WALL
                            collision[xpos][ypos] = false;
                            if ((currentItem == 'G') || (currentItem == 'g')) { // is
                                // GHOST
                                if (currentItem == 'G') {
                                    currentItem = '*';
                                }

                                GhostRespawnPoints.add(new Point(xpos, ypos));
                            } else if ((currentItem == 'R')
                                    || (currentItem == 'r')) { // is
                                // RespawnPoint
                                if (currentItem == 'R') {
                                    currentItem = '*';
                                }
                                RespawnPoints.add(new Point(xpos, ypos));
                            } else if ((currentItem == '-')
                                    || (currentItem == '_')) { // is GhostBlock
                                if (currentItem == '_') {
                                    currentItem = '*';
                                }
                                GhostBlock.add(new Point(xpos, ypos));
                            } else if (currentItem == '+') { // is SuperDot
                                //
                            } else if (currentItem == 'P') { // is Powerpill
                                //
                            } else if (currentItem == 'F') { // is Fruit
                                //
                            }

                            // check for Dot at last because other Items can
                            // have Dot below
                            if (currentItem == '*') { // is DOT
                                //
                            } else { // ZoneChange Point
                                if ((currentItem >= 48) && (currentItem <= 57)) {
                                    //
                                }
                            }
                        }
                    }
                    ypos++;
                } else // MAP INFO didn't start yet
                {
                    if (line.startsWith("x=")) {
                        x = Integer.parseInt(line.substring(2));
                    } else if (line.startsWith("y=")) {
                        y = Integer.parseInt(line.substring(2));
                    } else if (line.startsWith("y=")) {
                        y = Integer.parseInt(line.substring(2));
                    } else if (line.startsWith("dot_respawnTime=")) {
                        dot_respawnTime = Integer.parseInt(line.substring("dot_respawnTime=".length()));
                    } else if (line.startsWith("superdot_respawnTime=")) {
                        superdot_respawnTime = Integer.parseInt(line.substring("superdot_respawnTime=".length()));
                    } else if (line.startsWith("fruit_respawnTime=")) {
                        fruit_respawnTime = Integer.parseInt(line.substring("fruit_respawnTime=".length()));
                    } else if (line.startsWith("powerpill_respawnTime=")) {
                        powerpill_respawnTime = Integer.parseInt(line.substring("powerpill_respawnTime=".length()));
                    } else if (line.startsWith("dot_Score=")) {
                        dot_score = Integer.parseInt(line.substring("dot_Score=".length()));
                    } else if (line.startsWith("superdot_Score=")) {
                        superdot_score = Integer.parseInt(line.substring("superdot_Score=".length()));
                    } else if (line.startsWith("fruit_Score=")) {
                        fruit_score = Integer.parseInt(line.substring("fruit_Score=".length()));
                    } else if (line.startsWith("powerpill_Score=")) {
                        powerpill_score = Integer.parseInt(line.substring("powerpill_Score=".length()));
                    } else if (line.startsWith("mapchange=")) {
                        zoneChangeNames.add(line.substring("mapchange=".length()));
                    } else if (line.startsWith("superdot_probability=")) {
                        superdot_prob = Integer.parseInt(line.substring("superdot_probability=".length()));
                    } else if (line.startsWith("fruit_probability=")) {
                        fruit_prob = Integer.parseInt(line.substring("fruit_probability=".length()));
                    } else if (line.startsWith("powerpill_probability=")) {
                        powerpill_prob = Integer.parseInt(line.substring("powerpill_probability=".length()));
                    } else if (line.startsWith("MAPSTART")) {
                        mapstart = true;
                        collision = new boolean[x][y];
                        dots = new Dot[x][y];
                    }
                }
            }
        } catch (Exception e) {
            // TODO: throw the exception to the caller so it knows about the
            // problem.
            e.printStackTrace();
        }
    }

    @Override
    public void onInit() throws Exception {
        System.out.println("init zone :" + zonename);
    }

    @Override
    public void onFinish() throws Exception {
        // TODO Auto-generated method stub
    }

    public int isCollision(int x, int y) {
        // order is important : GhostBlock needs to be higher than Player
        
        if (collision[x][y]) {
            return Constants.COLLISION_WALL;
        }
        for (Point block : GhostBlock) {
            if ((block.getX() == x) && (block.getY() == y)) {
                return Constants.COLLISION_GHOSTBLOCK;
            }
        }
        for (RPObject player : Players) {
            if ((player.getInt("x") == x) && (player.getInt("y") == y)) {
                return Constants.COLLISION_PLAYER;
            }
        }
        for (RPObject ghost : Ghosts) {
            if ((ghost.getInt("x") == x) && (ghost.getInt("y") == y)) {
                return Constants.COLLISION_GHOST;
            }
        }
        return Constants.COLLISION_NONE;
    }

    public synchronized Dot eatDot(int x, int y) {
        if (dots[x][y] != null) {
            Dot res = dots[x][y];
            dots[x][y] = null;
            return res;
        }
        return null;

    }

    public synchronized void respawnDot(Dot object) {
        dots[object.getX()][object.getY()] = object;
    }

    public Point getFreeRespawnPoint(int type) {

        Random random = new Random();
        for (int i = 0; i < RespawnPoints.size(); i++) {
            Point point;
            // type = 0 is Player
            if (type == 0) {
                point = RespawnPoints.get(random.nextInt(RespawnPoints.size()));
            } else {
                point = GhostRespawnPoints.get(random.nextInt(GhostRespawnPoints.size()));
            }
            boolean free = true;
            for (RPObject player : Players) {
                if ((player.getInt("x") == point.getX())
                        && (player.getInt("y") == point.getY())) {
                    free = false;
                    break;
                }
            }
            for (RPObject ghost : Ghosts) {
                if ((ghost.getInt("x") == point.getX())
                        && (ghost.getInt("y") == point.getY())) {
                    free = false;
                    break;
                }
            }
            if (free) {
                return point;
            }
        }
        // if we are here, no free RespawnPoint has been found

        return null;
    }

    public synchronized boolean addPlayer(RPObject object) {
        boolean res = respawnMovable(object);
        if (res) {
            synchronized (Players) {
                Players.add(object);
            }
        }
        return res;

    }

    public synchronized boolean addGhost(RPObject object) {
        boolean res = respawnMovable(object);
        if (res) {
            Ghosts.add(object);
        }
        return res;

    }

    public synchronized void addWall(RPObject object) {
        world.add(object);
    }

    public synchronized void addDot(RPObject object) {
        int x = object.getInt("x");
        int y = object.getInt("y");
        if (object.get("type").equals(Constants.TYPE_DOT)) {
            dots[x][y] = new Dot(object);
        } else if (object.get("type").equals(Constants.TYPE_SUPERDOT)) {
            dots[x][y] = new Superdot(object);
        } else if (object.get("type").equals(Constants.TYPE_POWERPILL)) {
            dots[x][y] = new Powerpill(object);
        } else if (object.get("type").equals(Constants.TYPE_FRUIT)) {
            dots[x][y] = new Fruit(object);
        }
        respawnDots.remove(dots[object.getInt("x")][object.getInt("y")]);
        world.add(object);



    }

    public synchronized void updateDot(RPObject object) {
        if (object.getInt("respawn") > 1) {
            dots[object.getInt("x")][object.getInt("y")] = null;
            world.modify(object);
        } else{
            respawnDots.remove(dots[object.getInt("x")][object.getInt("y")]);
            world.modify(object);
        }
    }

    public synchronized void addZoneChange(RPObject object) {
        ZoneChangePoint point = new ZoneChangePoint(object);
        zoneChangePoints.add(point);
        world.add(point.getRPObject());
        ;
    }

    public synchronized void addSigns(RPObject object) {
        if (object.get("type").compareTo(Constants.TYPE_EATENSIGN) == 0) {
            EatenSign eatensign = new EatenSign(object.getInt("x"), object.getInt("y"), this);
            Signs.add(eatensign);
            world.add(eatensign.getRPObject());

        } else if (object.get("type").compareTo(Constants.TYPE_SCORESIGN) == 0) {
            ScoreSign scoresign = new ScoreSign(Constants.SCORE_EATENGHOST, object.getInt("x"), object.getInt("y"), this);
            Signs.add(scoresign);
            world.add(scoresign.getRPObject());

        }

    }

    public void removeSigns(RPObject object) {

        synchronized (Signs) {
            for (Sign sign : Signs) {
                if (sign.getRPObject().get("id").compareTo(object.get("id")) == 0) {
                    Signs.remove(sign);
                    world.remove(sign.getRPObject().getID());
                    break;
                }

            }
        }
    }

    private synchronized boolean respawnMovable(RPObject object) {
        Point respawnPoint = getFreeRespawnPoint(object.get("type").equals(
                Constants.TYPE_PLAYER) ? 0 : 1);
        if (respawnPoint != null) {
            object.put("x", (int) respawnPoint.getX());
            object.put("y", (int) respawnPoint.getY());
            object.put("dir", Constants.DIR_NONE);
            object.put("nextdir", Constants.DIR_NONE);
            object.put("zoneid", this.zonename);
            return true;
        } else {
            // try to add Player in next Turn
            addMovablesNextTurn.add(object);
            return false;
        }
    }

    public synchronized void beginTurn() {
//        if (this.zonename.compareTo("start")==0)
//            System.err.println("starting begin turn for start......inside :)");

        // check for Dots to respawn
        Vector<Dot> deleteDots = new Vector<Dot>();
        for (Dot dot : respawnDots) {
            if (dot.respawn()) { // add dot to Vector to delete later - can't
                // delete while in for()
                deleteDots.add(dot);
                respawnDot(dot);
                world.modify(dot.getRPObject());
            }
        }
        // delete respawned Dots from Vector
        for (Dot object : deleteDots) {
            respawnDots.remove(object);
        }

        // move Players and Ghosts
        movePlayers();
        moveGhosts();

        // change Zones
        for (zoneChangeNote note : zoneChangeNotes) {
            world.changeZone(note.object, this, note.newZone.getZoneName());
//                        world.modify(note.object);
        }
        zoneChangeNotes.clear();

        // add Players that could not been added
        Vector<RPObject> temp = new Vector<RPObject>();
        try {
            for (RPObject movable : addMovablesNextTurn) {
                respawnMovable(movable);
            }
            addMovablesNextTurn.clear();
        } catch (Exception e) {
            System.err.println("zone:beginturn exception occured " + addMovablesNextTurn);
            addMovablesNextTurn.clear();
        }
        for (RPObject player : temp) {
            addPlayer(player);
        }

        Vector<Sign> signtemp = new Vector<Sign>();
        // check EatenSigns
        for (Sign sign : Signs) {
            if (sign.checkDelete()) {
                world.remove(sign.getRPObject().getID());
                signtemp.add(sign);
            }
        }
        for (Sign sign : signtemp) {
            Signs.remove(sign);
        }
    }

    public synchronized void movePlayers() {
        for (RPObject object : Players) {
            // if Player is in powermode reduce powerturntime

            ZoneDivider.get().incrementPopulation(object);
            if (object.getInt("power") > 0) {
                object.put("power", object.getInt("power") - 1);
            }

            int nextDir = object.getInt("nextdir");
            int dir = object.getInt("dir");
                
            if (nextDir != Constants.DIR_NONE) {
                int collision = Constants.COLLISION_NONE;
                int x = object.getInt("x");
                int y = object.getInt("y");
                if (x == 0) {
                    x++;
                    object.put("x", x);

                }
                if (y == 0) {
                    y++;
                    object.put("y", y);
                }
                switch (nextDir) {
                    case Constants.DIR_NORTH:
                        collision = isCollision(x, y - 1);
                        break;
                    case Constants.DIR_SOUTH:
                        collision = isCollision(x, y + 1);
                        break;
                    case Constants.DIR_EAST:
                        collision = isCollision(x + 1, y);
                        break;
                    case Constants.DIR_WEST:
                        collision = isCollision(x - 1, y);
                        break;
                }

                if ((collision == Constants.COLLISION_NONE)
                        || (collision == Constants.COLLISION_GHOSTBLOCK)) {
                    object.put("dir", nextDir);
                    dir = nextDir;
                    object.put("nextdir", Constants.DIR_NONE);
                } else {
                    // if Player is not moving and want to move in a direction
                    // which is blocked. Stop all movements
                    if (dir == Constants.DIR_NONE) {
                        object.put("nextdir", Constants.DIR_NONE);
                        object.put("dir", Constants.DIR_NONE);
                    }
                }
            }

            switch (dir) {
                case Constants.DIR_NORTH:
                    movePlayer(object, 0, -1);
                    break;
                case Constants.DIR_SOUTH:
                    movePlayer(object, 0, 1);
                    break;
                case Constants.DIR_EAST:
                    movePlayer(object, 1, 0);
                    break;
                case Constants.DIR_WEST:
                    movePlayer(object, -1, 0);
                    break;
                case Constants.DIR_NONE:
                    movePlayer(object, 0, 0);
                    break;
            }
            world.modify(object);
        }
    }

    private synchronized void movePlayer(RPObject playerObject, int x, int y) {
        int collisionType = isCollision(playerObject.getInt("x") + x,
                playerObject.getInt("y") + y);
        int newX = playerObject.getInt("x") + x;
        int newY = playerObject.getInt("y") + y;
        // if no movement or no collision
        if (((x == 0) && (y == 0))
                || (collisionType == Constants.COLLISION_NONE)
                || (collisionType == Constants.COLLISION_GHOSTBLOCK)) {
            Dot dot;
            if ((dot = eatDot(newX, newY)) != null) {
                dot.eaten(playerObject);
                dot.addStats(world.getStats());
                playerObject.put("score", playerObject.getInt("score")
                        + dot.getScore());
                dot.setRespawnTime();
                respawnDots.add(dot);
                world.modify(dot.getRPObject());
            } else {
                for (ZoneChangePoint zonechange : zoneChangePoints) {
                    if (zonechange.isPlacedAt(newX, newY)) { // add to change
                        // Zone atfer
                        // turn
                        zoneChangeNotes.add(new zoneChangeNote(playerObject,
                                zonechange));
                    }
                }
//                                if(Players.size()>1 && newX>19 && zonename.compareTo("start")==0){
//                                                zoneChangeNotes.add(new zoneChangeNote(playerObject,
//								new ZoneChangePoint(this, zonename, -1, -1)));
//                                }
            }
            playerObject.put("x", newX);
            playerObject.put("y", newY);
        } else {
            switch (collisionType) {
                case Constants.COLLISION_WALL:
                case Constants.COLLISION_PLAYER:
                    playerObject.put("dir", Constants.DIR_NONE);
                    break;
                case Constants.COLLISION_GHOST:
                    if (playerObject.getInt("power") > 0) { // Player is in
                        // Powermode
                        playerObject.put("x", newX);
                        playerObject.put("y", newY);
                        killGhostAt(newX, newY, playerObject);
                        Dot dot;
                        if ((dot = eatDot(newX, newY)) != null) {
                            dot.eaten(playerObject);
                            dot.addStats(world.getStats());
                            playerObject.put("score", playerObject.getInt("score")
                                    + dot.getScore());
                            dot.setRespawnTime();
                            respawnDots.add(dot);
                            world.modify(dot.getRPObject());
                        }
                    } else {
                        playerObject.put("x", newX);
                        playerObject.put("y", newY);
                        world.modify(playerObject);
                        killPlayer(playerObject);
                        break;
                    }
            }
        }
        world.modify(playerObject);
    }

    private synchronized void killGhostAt(int newX, int newY, RPObject player) {
        for (RPObject ghost : Ghosts) {
            if ((ghost.getInt("x") == newX) && (ghost.getInt("y") == newY)) {
                killGhost(ghost, player);
            }
        }

    }

    private synchronized void killGhost(RPObject object, RPObject player) {
        player.put("score", player.getInt("score")
                + Constants.SCORE_EATENGHOST);
        world.getStats().add("killed Ghost", 1);
        EatenSign eatensign = new EatenSign(object.getInt("x"), object.getInt("y"), this);
        Signs.add(eatensign);
        ScoreSign scoresign = new ScoreSign(Constants.SCORE_EATENGHOST, object.getInt("x"), object.getInt("y"), this);
        Signs.add(scoresign);
        world.add(scoresign.getRPObject());
        world.add(eatensign.getRPObject());
        respawnMovable(object);
    }

    private synchronized void killPlayer(RPObject object) {
        int x = object.getInt("x");
        int y = object.getInt("y");
        world.getStats().add("killed Player", 1);
        int score = object.getInt("score");
        int newPlayerScore = (int) (score - score / 10);
        // if more than 200 Points are lost ... only take 200
        if (newPlayerScore < score - 200) {
            newPlayerScore = score - 200;
        }
        object.put("score", newPlayerScore);

        respawnMovable(object);

        object.put("dir", Constants.DIR_NONE);
        world.modify(object);

        EatenSign sign = new EatenSign(x, y, this);
        Signs.add(sign);
        world.add(sign.getRPObject());
    }

    public synchronized void moveGhosts() {
        // save CPU ?? don't move ghosts when no Player in world
        if (!world.moveGhosts()) {
            return;
        }

        for (RPObject ghost : Ghosts) {
            // check if Ghost should change Direction
            if(!ghost.has("!changedir"))
                ghost.put("!changedir", 0);
            if (ghost.getInt("!changedir") == 0) {
                ghost.put("dir", randomgen.nextInt(4));
                ghost.put("!changedir", randomgen.nextInt(6) + 1);
            } else {
                ghost.put("!changedir", ghost.getInt("!changedir") - 1);
            }

            // move Ghost
            int nextDir = ghost.getInt("nextdir");
            if (nextDir != Constants.DIR_NONE) {
                int collision = Constants.COLLISION_NONE;
                int x = ghost.getInt("x");
                int y = ghost.getInt("y");
                switch (nextDir) {
                    case Constants.DIR_NORTH:
                        collision = isCollision(x, y - 1);
                        break;
                    case Constants.DIR_SOUTH:
                        collision = isCollision(x, y + 1);
                        break;
                    case Constants.DIR_EAST:
                        collision = isCollision(x + 1, y);
                        break;
                    case Constants.DIR_WEST:
                        collision = isCollision(x - 1, y);
                        break;
                }

                if ((collision == Constants.COLLISION_NONE)
                        || (collision == Constants.COLLISION_PLAYER)) {
                    ghost.put("dir", nextDir);
                    ghost.put("nextdir", Constants.DIR_NONE);
                }

            }

            switch (ghost.getInt("dir")) {
                case Constants.DIR_NORTH:
                    moveGhost(ghost, 0, -1);
                    break;
                case Constants.DIR_SOUTH:
                    moveGhost(ghost, 0, 1);
                    break;
                case Constants.DIR_EAST:
                    moveGhost(ghost, 1, 0);
                    break;
                case Constants.DIR_WEST:
                    moveGhost(ghost, -1, 0);
                    break;
            }

            world.modify(ghost);
        }
    }

    private synchronized void moveGhost(RPObject ghost, int x, int y) {
        int collisionType = isCollision(ghost.getInt("x") + x, ghost.getInt("y")
                + y);
        if ((collisionType == Constants.COLLISION_NONE)
                || (collisionType == Constants.COLLISION_PLAYER)) {
            int newX = ghost.getInt("x") + x;
            int newY = ghost.getInt("y") + y;
            ghost.put("x", newX);
            ghost.put("y", newY);
            if (collisionType == Constants.COLLISION_PLAYER) {
                for (RPObject player : Players) {
                    if ((player.getInt("x") == newX)
                            && (player.getInt("y") == newY)) {
                        if (player.getInt("power") > 0) { // Player is in
                            // Powermode
                            ghost.put("x", newX);
                            ghost.put("y", newY);
                            world.modify(ghost);
                            killGhostAt(newX, newY, player);
                        } else {
                            killPlayer(player);
                            break;
                        }
                        break;
                    }
                }
            }
        } else {
            ghost.put("!changedir", 0);
        }

    }

    public synchronized void removePlayer(RPObject.ID id) {
        for (RPObject player : Players) {
            if (player.getID().getObjectID() == id.getObjectID()) {
                Players.remove(player);
                break;
            }
        }

    }

    public synchronized void removeGhost(RPObject.ID id) {
        for (RPObject ghost : Ghosts) {
            if (ghost.getID().getObjectID() == id.getObjectID()) {
                Ghosts.remove(ghost);
                break;
            }
        }

    }

    public String getName() {
        return zonename;
    }

    public Vector<RPObject> getPlayers() {
        return Players;
    }

    public boolean hasPlayers() {

        return (getPlayers().size() > 0);
    }

    public int getDotRespawnTime() {
        return dot_respawnTime;
    }

    public int getSuperDotRespawnTime() {
        return superdot_respawnTime;
    }

    public Random getRandom() {
        return randomgen;
    }

    public int getPowerpillRespawnTime() {
        return powerpill_respawnTime;
    }

    public int getFruitRespawnTime() {
        return fruit_respawnTime;
    }

    public MaPacmanRPWorld getWorld() {
        return world;
    }
}

class zoneChangeNote {

    ZoneChangePoint newZone;
    RPObject object;

    public zoneChangeNote(RPObject object, ZoneChangePoint newZone) {
        this.newZone = newZone;
        this.object = object;
    }
}
